<?php

namespace tests\Unit;

use think\App;
use think\facade\Db;
use think\facade\Config;
use think\exception\HttpResponseException;
use app\admin\library\Auth;
use app\admin\model\Admin;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;

class AdminControllerTestCase extends TestCase
{
    protected ?App $app = null;
    protected $request;
    protected ?MockObject $mockAuth = null;
    protected ?Admin $admin = null;

    protected function setUp(): void
    {
        parent::setUp();
        
        // 初始化应用
        $this->initializeApp();
        
        // 开始数据库事务
        Db::startTrans();
        
        // 创建测试数据和 Mock 对象
        $this->createTestFixtures();
    }

    protected function tearDown(): void
    {
        // 回滚数据库事务
        Db::rollback();
        
        // 清理资源
        $this->cleanupResources();
        
        parent::tearDown();
    }

    /**
     * 初始化应用
     */
    private function initializeApp(): void
    {
        $this->app = new App();
        $this->app->initialize();
        
        // 设置测试环境
        $this->app->env->set('APP_DEBUG', true);
        $this->app->env->set('APP_ENV', 'test');
        
        // 配置数据库
        Config::set(require __DIR__ . '/../../config/test.php', 'database');
        
        $this->request = $this->app->request;
    }

    /**
     * 创建测试夹具
     */
    private function createTestFixtures(): void
    {
        // 创建测试管理员
        $this->admin = Admin::create([
            'username' => 'test_' . uniqid(),
            'nickname' => '测试管理员',
            'password' => password_hash('123456', PASSWORD_DEFAULT),
            'email' => 'test' . uniqid() . '@example.com',
            'mobile' => '138' . rand(10000000, 99999999),
            'status' => 'enable'
        ]);
        
        // 创建 Mock Auth 对象
        $this->createMockAuth();
    }

    /**
     * 创建 Mock Auth 对象
     */
    private function createMockAuth(): void
    {
        $this->mockAuth = $this->createMock(Auth::class);
        
        // 配置 Mock 对象的行为
        $this->mockAuth
            ->method('isLogin')
            ->willReturn(true);
            
        $this->mockAuth
            ->method('isSuperAdmin')
            ->willReturn(true);
            
        $this->mockAuth
            ->method('check')
            ->willReturn(true);
            
        $this->mockAuth
            ->method('getInfo')
            ->willReturn([
                'id' => $this->admin->id,
                'username' => $this->admin->username,
                'nickname' => $this->admin->nickname
            ]);
            
        $this->mockAuth
            ->method('getAdminChildGroups')
            ->willReturn([]);
            
        $this->mockAuth
            ->method('getGroupAdmins')
            ->willReturn([]);
        
        // 模拟魔术方法 __get
        $this->mockAuth
            ->method('__get')
            ->willReturnCallback(function($name) {
                return match($name) {
                    'id' => $this->admin->id,
                    'username' => $this->admin->username,
                    'nickname' => $this->admin->nickname,
                    default => $this->admin->$name ?? null
                };
            });
    }

    /**
     * 创建控制器并注入依赖
     */
    protected function createController(string $controllerClass): object
    {
        // 使用反射创建控制器实例，避免调用构造函数
        $reflection = new \ReflectionClass($controllerClass);
        $controller = $reflection->newInstanceWithoutConstructor();
        
        // 手动设置必要的属性
        $this->initializeControllerProperties($controller);
        
        // 注入依赖
        $this->injectDependencies($controller);
        
        // 手动初始化模型属性
        $this->initializeModelProperty($controller);
        
        return $controller;
    }

    /**
     * 初始化控制器属性
     */
    private function initializeControllerProperties(object $controller): void
    {
        $reflection = new \ReflectionClass($controller);
        
        // 设置app属性
        if ($reflection->hasProperty('app')) {
            $appProperty = $reflection->getProperty('app');
            $appProperty->setAccessible(true);
            $appProperty->setValue($controller, $this->app);
        }
        
        // 设置request属性
        if ($reflection->hasProperty('request')) {
            $requestProperty = $reflection->getProperty('request');
            $requestProperty->setAccessible(true);
            $requestProperty->setValue($controller, $this->request);
        }
    }

    /**
     * 注入依赖到控制器
     */
    private function injectDependencies(object $controller): void
    {
        $reflection = new \ReflectionClass($controller);
        
        // 注入 Mock Auth
        if ($reflection->hasProperty('auth')) {
            $authProperty = $reflection->getProperty('auth');
            $authProperty->setAccessible(true);
            $authProperty->setValue($controller, $this->mockAuth);
        }
    }

    /**
     * 初始化模型属性
     */
    private function initializeModelProperty(object $controller): void
    {
        $reflection = new \ReflectionClass($controller);
        
        // 根据控制器类型初始化对应的模型
        if ($reflection->hasProperty('model')) {
            $modelProperty = $reflection->getProperty('model');
            $modelProperty->setAccessible(true);
            
            $controllerName = $reflection->getShortName();
            $modelClass = "\\app\\admin\\model\\{$controllerName}";
            
            if (class_exists($modelClass)) {
                $modelProperty->setValue($controller, new $modelClass());
            }
        }
    }

    /**
     * 模拟 POST 请求数据
     */
    protected function mockPostData(array $data): void
    {
        $_POST = $data;
        $this->request->setMethod('POST');
        $this->request = $this->request->withPost($data);
    }

    /**
     * 模拟 GET 请求参数
     */
    protected function mockGetData(array $data): void
    {
        $_GET = $data;
        $this->request->setMethod('GET');
        $this->request = $this->request->withGet($data);
    }

    /**
     * 执行控制器方法并处理异常
     */
    protected function executeControllerAction(callable $action): array
    {
        try {
            $result = $action();
            
            // 如果方法直接返回数组，返回它
            if (is_array($result)) {
                return $result;
            }
            
            // 如果没有抛出异常，说明可能有问题
            $this->fail('Expected HttpResponseException was not thrown');
            return []; // 添加返回值，虽然这行不会执行到
            
        } catch (HttpResponseException $e) {
            $response = $e->getResponse();
            return $response->getData();
        }
    }

    /**
     * 断言成功响应
     */
    protected function assertSuccessResponse(array $response, string $message = ''): void
    {
        $this->assertArrayHasKey('code', $response, '响应应该包含 code 字段');
        $this->assertEquals(1, $response['code'], '成功响应的 code 应该为 1');
        
        if ($message !== null) {
            $this->assertArrayHasKey('msg', $response, '响应应该包含 msg 字段');
            $this->assertStringContainsString($message, $response['msg']);
        }
    }

    /**
     * 断言错误响应
     */
    protected function assertErrorResponse(array $response, string $message = ''): void
    {
        $this->assertArrayHasKey('code', $response, '响应应该包含 code 字段');
        $this->assertEquals(0, $response['code'], '错误响应的 code 应该为 0');
        
        if ($message !== null) {
            $this->assertArrayHasKey('msg', $response, '响应应该包含 msg 字段');
            $this->assertStringContainsString($message, $response['msg']);
        }
    }

    /**
     * 断言数据库记录存在
     */
    protected function assertDatabaseHas(string $table, array $data): void
    {
        $count = Db::table($table)->where($data)->count();
        $this->assertGreaterThan(0, $count, "数据库表 {$table} 中应该存在匹配的记录");
    }

    /**
     * 断言数据库记录不存在
     */
    protected function assertDatabaseMissing(string $table, array $data): void
    {
        $count = Db::table($table)->where($data)->count();
        $this->assertEquals(0, $count, "数据库表 {$table} 中不应该存在匹配的记录");
    }

    /**
     * 清理资源
     */
    private function cleanupResources(): void
    {
        $this->app = null;
        $this->request = null;
        $this->mockAuth = null;
        $this->admin = null;
        
        // 清理全局变量
        $_POST = [];
        $_GET = [];
    }

    /**
     * 获取测试管理员
     */
    protected function getTestAdmin(): Admin
    {
        return $this->admin;
    }

    /**
     * 获取 Mock Auth 对象
     */
    protected function getMockAuth(): MockObject
    {
        return $this->mockAuth;
    }
} 